import {
  RawMessage,
  Player,
  world,
  ItemStack,
  system,
} from "@minecraft/server";
import { ActionFormData, MessageFormData } from "@minecraft/server-ui";

/**
 * Create an article.
 */
export class Article {
  constructor(
    public readonly id: string,
    public title: string | RawMessage,
    public body: string | RawMessage,
    public iconPath?: string,
    public needUnlock = true
  ) {
    this.register();
  }
  /**
   * Display the reading to a player.
   */
  display(player: Player): void {
    if (!this.checkUnlock(player)) this.unlock(player);
    const FORM = new ActionFormData()
      .title(this.title)
      .body(this.body)
      .button({ translate: "gui.ok" });
    FORM.show(player);
  }
  /**
   * Registry the article to game.
   */
  protected register() {
    world.afterEvents.itemUse.subscribe((event) => {
      if (event.itemStack.typeId === this.id) this.display(event.source);
    });
  }
  /**
   * Unlock the article to article collection.
   * @param player
   */
  unlock(player: Player) {
    player.addTag(`articleUnlock:${this.id}`);
  }
  /**
   * Check the article whether it is unlocked or not to article collection.
   * @param player
   */
  checkUnlock(player: Player) {
    if (!this.needUnlock) {
      return true;
    } else {
      return player.hasTag(`articleUnlock:${this.id}`);
    }
  }
}

/**
 * Create an article with chapters.
 */
export class ChapterArticle extends Article {
  constructor(
    public readonly id: string,
    public title: string | RawMessage,
    public body: string | RawMessage,
    public chapters: ChapterData[],
    public iconPath?: string,
    public needUnlock = true
  ) {
    super(id, title, body, iconPath, needUnlock);
    this.register();
  }
  /**
   * Display the reading to a player.
   */
  display(player: Player): void {
    if (!this.checkUnlock(player)) this.unlock(player);
    const FORM = new ActionFormData().title(this.title).body(this.body);
    this.chapters.forEach((chapter) => {
      FORM.button(chapter.title, chapter.iconPath);
    });
    FORM.show(player).then((response) => {
      if (response.canceled || response.selection === undefined) {
        return;
      }
      const CHAPTER_FORM = new ActionFormData()
        .title(this.chapters[response.selection].title)
        .body(this.chapters[response.selection].body)
        .button({ translate: "gui.ok" });
      CHAPTER_FORM.show(player);
    });
  }
}

/**
 * Create an article collection.
 * It likes {@link ChapterArticle}, but support article unlock.
 */
export class ArticleCollection {
  constructor(
    public readonly id: string,
    public title: string | RawMessage,
    public body: string | RawMessage,
    public displayCondition: DisplayCondition,
    public articles: (Article | ChapterArticle)[]
  ) {
    this.register();
  }
  /**
   * Display the reading to a player.
   */
  display(player: Player): void {
    let articleList: (Article | ChapterArticle)[] = [];
    const MAIN_FORM = new ActionFormData().title(this.title).body(this.body);
    this.articles.forEach((article) => {
      if (article.checkUnlock(player)) {
        MAIN_FORM.button(article.title, article.iconPath);
        articleList.push(article);
      }
    });
    if (articleList.length === 0) {
      const WARNING_FORM = new MessageFormData()
        .title({ translate: "article.nothing.title" })
        .body({ translate: "article.nothing.body" })
        .button1({ translate: "gui.ok" })
        .button2({ translate: "gui.close" });
      WARNING_FORM.show(player);
    } else {
      MAIN_FORM.show(player).then((response) => {
        if (response.canceled || response.selection === undefined) {
          return;
        }
        articleList[response.selection].display(player);
      });
    }
  }
  /**
   * Registry the article to game.
   */
  protected register() {
    if (this.displayCondition.default === true) {
      world.afterEvents.itemUse.subscribe((event) => {
        if (event.itemStack.typeId === this.id) this.display(event.source);
      });
    }
    if (this.displayCondition.firstLoad === true) {
      world.afterEvents.playerSpawn.subscribe((event) => {
        if (event.initialSpawn) this.display(event.player);
      });
    }
    if (this.displayCondition.itemStack) {
      world.afterEvents.itemUse.subscribe((event) => {
        if (event.itemStack.typeId === this.displayCondition.itemStack?.typeId)
          this.display(event.source);
      });
    }
    if (this.displayCondition.command) {
      if (typeof this.displayCondition.command === "string") {
        world.beforeEvents.chatSend.subscribe((event) => {
          if (event.message === this.displayCondition.command) {
            event.cancel = true;
            system.runTimeout(() => {
              this.display(event.sender);
            }, 40);
          }
        });
      }
      if (this.displayCondition.command instanceof Array) {
        world.beforeEvents.chatSend.subscribe((event) => {
          if (this.displayCondition.command?.includes(event.message)) {
            event.cancel = true;
            system.runTimeout(() => {
              this.display(event.sender);
            }, 40);
          }
        });
      }
    }
  }
}

/**
 * Data of chapters.
 */
export interface ChapterData {
  /**
   * Title of the chapter.
   */
  title: string | RawMessage;
  /**
   * Body of the Chapter.
   */
  body: string | RawMessage;
  /**
   * Icon path of the chapter.
   */
  iconPath?: string;
}

/**
 * Display condition of articles.
 */
export interface DisplayCondition {
  /**
   * Default condition like simple articles.
   */
  default?: boolean;
  /**
   * Display article when player use the item.
   */
  itemStack?: ItemStack;
  /**
   * Display article when player send the command.
   */
  command?: string | string[];
  /**
   * Display article when the world first load.
   */
  firstLoad?: boolean;
}
